package cn.dengta.webapp.base.util;

import java.io.InputStream;
import java.net.InetAddress;
import java.util.*;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;

import cn.dengta.common.util.HttpUtil;
import cn.dengta.common.util.SpringUtil;
import cn.dengta.context.model.Language;
import cn.dengta.context.web.RequestContextImpl;
import com.maxmind.geoip2.DatabaseReader;
import com.maxmind.geoip2.model.AsnResponse;
import com.maxmind.geoip2.model.CountryResponse;
import com.maxmind.geoip2.record.Country;
import lombok.extern.slf4j.Slf4j;
import me.codeplayer.util.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;

@Slf4j
public abstract class IpHelper {

	static final String URL = "https://oss.flala6.com/internal/ip/china-ip.txt";
	/* geoIP解析 */
	static DatabaseReader GEO_IP2_READER;
	static DatabaseReader GEO_IP2_ASN;

	// 只存放属于中国的IP段
	static TreeMap<Long, Long> chinaIpRangeMap;
	static long nextUpdate;

	static {
		initChinaIps();
		initGeoIpMap();
	}

	@Nullable
	public static TreeMap<Long, Long> importChinaIps() {
		String response = null;
		try {
			response = HttpUtil.doGet(URL, null);
			final TreeMap<Long, Long> map = new TreeMap<>();

			// "apnic|CN|ipv4|1.0.2.0|512|20110414|allocated"
			response.lines().forEach(line -> {
				// 只处理属于中国的IPv4地址
				if (line.startsWith("apnic|CN|ipv4|")) {
					String[] parts = line.split("\\|");
					String ip = parts[3];
					int count = Integer.parseInt(parts[4]);
					final long ipBegin = ip2Long(ip);
					map.put(ipBegin, ipBegin + count);
				}
			});
			return map;
		} catch (Exception e) {
			log.error("导入中国大陆IP时出错：response=\n" + response, e);
			return null;
		}
	}

	public static void initChinaIps() {
		final TreeMap<Long, Long> map = importChinaIps();
		if (map != null && !map.isEmpty()) {
			chinaIpRangeMap = map;
		}
	}

	public static long ip2Long(String ipv4) {
		String[] parts = ipv4.split("\\.");
		if (parts.length != 4) {
			return -1;
		}
		long val = 0;
		for (int i = 0; i < parts.length; i++) {
			int part = Integer.parseInt(parts[i]);
			val += part * Math.pow(256, 3 - i);
		}
		return val;
	}

	/**
	 * 指示该IP是否来自中国大陆
	 */
	public static boolean fromChina(String ip) {
		final TreeMap<Long, Long> map = chinaIpRangeMap;
		if (map == null || map.isEmpty()) {
			// 为了避免单次请求失败，失败后每5分钟尝试更新一次，直到成功为止
			final long now = System.currentTimeMillis();
			if (nextUpdate < now) {
				nextUpdate = now + 5 * EasyDate.MILLIS_OF_MINUTE;
				SpringUtil.asyncExecute(IpHelper::initChinaIps);
			}
			return false;
		}
		if (StringUtils.isEmpty(ip)) {
			return false;
		}
		final long ipLong = ip2Long(ip);
		if (ipLong < 0) {
			return false;
		}
		final Map.Entry<Long, Long> entry = map.floorEntry(ipLong);
		return entry != null && entry.getKey() <= ipLong && ipLong < entry.getValue();
	}

	/**
	 * 是否需要拒绝当前IP访问（目前是登录）
	 */
	public static boolean shouldDenyIp(HttpServletRequest request) {
		final Language language = RequestContextImpl.doGetLanguage(request);
//		return language == Language.zh_CN && fromChina(RequestContextImpl.doGetClientIP(request));
		return false;
	}

	public static void assertIpValid(HttpServletRequest request, boolean openMode) {
		if (openMode) {
			Assert.notTrue(shouldDenyIp(request), "不支持中国大陆登录");
		}
	}

	static void initGeoIpMap() {
		try {
			final InputStream ins = Thread.currentThread().getContextClassLoader().getResourceAsStream("GeoLite2-Country.mmdb");
			final InputStream asnIns = Thread.currentThread().getContextClassLoader().getResourceAsStream("GeoLite2-ASN.mmdb");
			// 读取数据库内容
			GEO_IP2_READER = new DatabaseReader.Builder(ins).build();
			GEO_IP2_ASN = new DatabaseReader.Builder(asnIns).build();
		} catch (Exception e) {
			log.error("读取geoip2文件时出错");
		}
	}

	public static Pair<String, String> getCountry(String ip) {
		Assert.isTrue(StringUtil.notEmpty(ip));
		DatabaseReader reader = GEO_IP2_READER;
		if (reader == null) {
			SpringUtil.asyncRun(IpHelper::initGeoIpMap, "读取geoip2文件");
			return null;
		}
		try {
			// 获取查询结果
			Optional<CountryResponse> response = reader.tryCountry(InetAddress.getByName(ip)); // 要解析的ip地址
			if (response.isPresent()) {
				Country country = response.get().getCountry();
				return Pair.of(country.getIsoCode(), country.getNames().get("en"));
			}
		} catch (Exception e) {
			log.error("根据IP读取geoip2文件国家出错");
			return null;
		}
		return null;
	}

	public static Pair<String, String> getASN(String ip) {
		Assert.isTrue(StringUtil.notEmpty(ip));
		DatabaseReader reader = GEO_IP2_ASN;
		if (reader == null) {
			SpringUtil.asyncRun(IpHelper::initGeoIpMap, "读取geoip2文件");
			return null;
		}
		try {
			// 获取查询结果
			Optional<AsnResponse> response = reader.tryAsn(InetAddress.getByName(ip)); // 要解析的ip地址
			if (response.isPresent()) {
				AsnResponse asnResponse = response.get();
				return Pair.of(asnResponse.getIpAddress(), asnResponse.getAutonomousSystemOrganization());
			}
		} catch (Exception e) {
			log.error("根据IP读取geoip2文件ASN出错");
			return null;
		}
		return null;
	}

}
